var express = require('express');
var bodyParser = require('body-parser');
var fs = require('fs');
var path = require('path');
var https = require('https');
var http = require('http');
var fileupload = require("express-fileupload");
var format = require('string-format');
format.extend(String.prototype);
var shelljs = require('shelljs');
var mqtt = require('mqtt');
var { v4: uuidv4 } = require('uuid');
var kurento = require("kurento-client");
const { timeStamp } = require('console');

/*
cnpm install express -S
cnpm install body-parser -S
cnpm install express-fileupload -S
cnpm install string-format -S
cnpm install shelljs -S
cnpm install mqtt -S
cnpm install uuid -S
cnpm install kurento-client -S
*/

console.log('load libs success.');
// rpc message data struct
class TRpcMsg {
    constructor() {
        this.msgId = uuidv4().replace(/-/g, '');
        // msgType call , response , notify
        this.msgType = "call";
        this.method = "";
        this.params = {};
        this.result = null;
        this.error = {
            code: 0,
            msg: ""
        };
        this.responseTopic = "";
        this.senderId = "";
    };
    static getCallMsg(method, params, respTopic) {
        var me = this;
        var msg = new TRpcMsg();
        try {
            msg.msgType = "call";
            msg.senderId = me.senderId;
            msg.method = method;
            msg.params = params || {};
            msg.callRespTopic = respTopic;
        } catch (er) {
            console.log(er);
        }
        return msg;
    };
    static getNotifyMsg(method, params) {
        var me = this;
        var msg = new TRpcMsg();
        try {
            msg.msgType = "notify";
            msg.senderId = me.senderId;
            msg.method = method;
            msg.params = params || {};
        } catch (er) {
            console.log(er);
        }
        return msg;
    };
    static getRespMsg(callMsg, result, error) {
        var me = this;
        var msg = new TRpcMsg();
        try {
            msg.msgType = "response";
            msg.method = callMsg.method;
            msg.msgId = callMsg.msgId;
            msg.senderId = callMsg.senderId;
            msg.error = (error && error != null) ? error : {
                code: 0,
                msg: ''
            };
            msg.result = result || {};
        } catch (er) {
            console.log(er);
        }
        return msg;
    };
};

class TRpcApiSvr {
    constructor() {
        this.config = {
            port: 20081,
            domain: "dq.hhdata.cn",
            https: {
                enable: false,
                keyFn: "d:/project/hhrpcv10/svr/datas/keys/6097777_dq.hhdata.cn.key",
                certFn: "d:/project/hhrpcv10/svr/datas/keys/6097777_dq.hhdata.cn.pem"
            },
            statics: {
                "/": "d:/project"
            }
        };
        this.app = null;
        this.server = null;
        this.routers = {};
    };
    start() {
        var me = this;
        return new Promise((success, faild) => {
            try {
                var app = express();
                /*
                 * 静态虚拟目录
                 * */
                for (var sp in me.config.statics) {
                    //var webPath = me.statics[sp];
                    var webPath = me.config.statics[sp];
                    var virPath = sp.indexOf("/") == 0 ? sp : "/" + sp;
                    app.use(virPath, express.static(webPath));
                }
                /*
                 * 动态路由
                 * */
                var urlencodedParser = bodyParser.urlencoded({ extended: false });
                app.use(urlencodedParser);
                app.use(bodyParser.json());
                app.use(fileupload());
                var jsonParser = bodyParser.json();
                app.all("/rpc2022", jsonParser, function(req, resp) {
                    try {
                        var params = {};
                        var getParams = () => {
                            return new Promise((s, f) => {
                                req.setEncoding('utf8');
                                req.on('data', function(data) {
                                    try {
                                        var obj = JSON.parse(data);
                                        Object.keys(obj).forEach(k => {
                                            params[k] = obj[k];
                                        })
                                    } catch (ee) {
                                        console.log(ee)
                                    }
                                });
                                req.on('end', function() {
                                    s(params);
                                });
                                Object.keys(req.body).forEach(k => {
                                    params[k] = req.body[k];
                                })
                                Object.keys(req.query).forEach(k => {
                                    params[k] = req.query[k];
                                })
                                Object.keys(req.params).forEach(k => {
                                    params[k] = req.params[k];
                                })
                                s(params);
                            });
                        }
                        if (req.method == "OPTIONS") {
                            resp.setHeader('Access-Control-Allow-Headers', 'Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With');
                            resp.header("Access-Control-Allow-Origin", "*");
                            resp.header("Access-Control-Allow-Headers", "*");
                            resp.send(200);
                        } else if (req.method == "POST" || req.method == "GET") {
                            resp.setHeader('Access-Control-Allow-Headers', 'Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With');
                            resp.header("Access-Control-Allow-Origin", "*");
                            resp.header("Access-Control-Allow-Headers", "*");
                            getParams().then(_ => {
                                console.log(params);
                                try {
                                    var rpcMsg = JSON.parse(params.rpcMsg);
                                    var flag = rpcMsg && rpcMsg.msgType && rpcMsg.msgType == "call" && rpcMsg.method &&
                                        me.routers[rpcMsg.method] && me.routers[rpcMsg.method] != null;
                                    if (flag) {
                                        var handler = me.routers[rpcMsg.method];
                                        var respMsg = TRpcMsg.getRespMsg(rpcMsg, {}, { code: 0, msg: '' });
                                        handler(rpcMsg, req, resp, respMsg).then(_ => {
                                            var content = JSON.stringify(respMsg);
                                            resp.send(content);
                                        });
                                    } else {
                                        resp.send('rpc-error')
                                    }
                                } catch (error) {
                                    console.log(error);
                                    resp.send('rpc-error')
                                }
                            })
                        }
                    } catch (er) {
                        console.log(er);
                    }
                });
                var options = {};
                try {
                    if (me.config.https.enable) {
                        options = {
                            key: fs.readFileSync(me.config.https.keyFn),
                            cert: fs.readFileSync(me.config.https.certFn)
                        };
                    }
                } catch (eer) {
                    console.log(eer);
                }

                var webServer = me.config.https.enable ? https.createServer(options, app) : http.createServer(options, app);

                webServer.listen(me.config.port, function(err) {
                    if (err) {
                        console.log('start web error.', err);
                    } else {
                        me.app = app;
                        me.server = webServer;
                        var msg = "start web server on :{protocol}://{domain}:{port}/rpc2022";
                        msg = msg.format({
                            protocol: me.config.https.enable ? "https" : "http",
                            port: me.config.port,
                            domain: me.config.domain
                        })
                        console.log(msg);
                    }
                    success();
                });
            } catch (e) {
                console.log(e);
                success();
            }
        })

    };
    registRoute(method, handler) {
        var me = this;
        try {
            me.routers[method] = (callRpcData, request, response, respRpcData) => {
                return new Promise((success, faild) => {
                    handler(callRpcData, request, response, respRpcData).then(_ => {
                        success();
                    });
                })
            }
        } catch (error) {
            console.log(error);
        }
    };

}

var rpcApiSvr = new TRpcApiSvr();
rpcApiSvr.registRoute("/a/b/c", (callRpcData, request, response, respRpcData) => {
    return new Promise((success, faild) => {
        var ps = callRpcData.params;
        var result = {
            status: '1',
            ps: ps
        };
        respRpcData.result = result;
        success();
    });
});

var msg = new TRpcMsg();
msg.msgType = "call";
msg.method = "/a/b/c";
msg.params = {
    pid: 23,
    pcode: "abc",
    pname: "nickName"
};
var c = JSON.stringify(msg);
console.log(c);

rpcApiSvr.start();